package com.lz.ub.hm

import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbDeviceConnection
import androidx.collection.ArrayMap
import com.felhr.usbserial.UsbSerialDevice
import com.lz.ub.Logger
import java.util.*

/**
 * @author you
 * 蓝牙核心操作类, 可以自己维护也可用#UBCommandManager类
 */
class UBCommandOperator(
    private val connectTimeout: Int,
    private val baudRate: Int,
    private val dataBits: Int,
    private val stopBits: Int,
    private val parity: Int,
    private val flowControl: Int,
    private val readFactory: UBReader.UBReaderFactory,
    private val linkManager: UBLinkManager
) {

    companion object {
        const val TAG = "UBCommandOperator"
    }

    //当前所有设备
    private val devices = ArrayMap<Int, UBDevice>(32)
    //当前所有设备的mac
    private val macs = HashSet<String>(32)
    //指令处理器
    private var ubCommand: UBCommand? = null
    //读取回调
    private val readCallbacks = ArrayList<UBReadCallback>()
    //扫描回调
    private val scanCallbacks = ArrayList<UBScanCallback>()

    fun isOpened(): Boolean = ubCommand != null

    fun isAvailable(): Boolean = isOpened() && ubCommand!!.isAvailable()

    fun hasLink(link: Int): Boolean = devices.contains(link)

    fun open(usbDevice: UsbDevice, conn: UsbDeviceConnection) {
        ubCommand = openDevice(usbDevice, conn)
    }

    fun close() {
        for (entry in devices.entries) {
            entry.value.close()
        }
        ubCommand?.close()
        ubCommand = null
    }

    fun reset(resetCommand: String) {
        if (isOpened()) {
            ubCommand?.write(resetCommand)
        }
    }

    fun resetAndClearDevices(resetCommand: String) {
        reset(resetCommand)
        devices.clear()
        macs.clear()
        linkManager.reset()
        ubCommand?.reset()
    }

    fun findDevice(link: Int): UBDevice? = devices[link]

    fun addDevice(mac: String, property: Int, addType: Int, tag: Any? = null): UBDevice? {
        if (macs.contains(mac)) {
            Logger.i(TAG, "addDevice repeat mac = $mac")
            return null
        }
        return linkManager.get().takeIf { Logger.i(TAG, "addDevice get link = $it"); it != INVALID }?.let {
            macs.add(mac)
            UBDevice(mac, it, property, addType, tag).apply { devices[it] = this }
        }
    }

    // 注意删除此项前要确定此设备已经断开
    fun removeDevice(link: Int) {
        devices.remove(link)?.also { linkManager.remove(link); macs.remove(it.mac) }
    }

    fun connectAll(callback: UBCallback? = null) {
        if (isAvailable()) {
            val deviceList = LinkedList<UBDevice>()
            for (entry in devices.entries) {
                if (!entry.value.isEnabled()) deviceList.add(entry.value)
            }
            ubCommand?.connect(deviceList, callback)
        }
    }

    fun connect(link: Int, callback: UBCallback? = null) {
        if (isAvailable()) {
            devices[link]?.takeIf { !it.isEnabled() }?.also { ubCommand?.connect(it, callback) }
        }
    }

    fun disconnectAll(callback: UBCallback? = null) {
        if (isAvailable()) {
            val deviceList = LinkedList<UBDevice>()
            for (entry in devices.entries) {
                if (entry.value.isConnecting()) deviceList.add(entry.value)
            }
            Logger.i(TAG, "deviceList = $deviceList")
            ubCommand?.disconnect(deviceList, callback)
        }
    }

    fun disconnect(link: Int, callback: UBCallback? = null) {
        if (isAvailable()) {
            devices[link]?.takeIf { it.isConnecting() }?.also { ubCommand?.disconnect(it, callback) }
        }
    }

    fun pushCommand(command: String) {
        if (isAvailable()) {
            ubCommand?.pushCommand(command)
        } else {
            Logger.i(TAG, "pushCommands error $command, UBCommandOperator is not available")
        }
    }

    fun pushCommand(link: Int, command: String) {
        if (hasLink(link)) {
            pushCommand(command)
        }
    }

    fun addReadCallback(readCallback: UBReadCallback) {
        readCallbacks.add(readCallback)
    }

    fun removeReadCallback(readCallback: UBReadCallback) {
        readCallbacks.remove(readCallback)
    }

    fun addScanCallback(scanCallback: UBScanCallback) {
        scanCallbacks.add(scanCallback)
    }

    fun removeScanCallback(scanCallback: UBScanCallback) {
        scanCallbacks.remove(scanCallback)
    }

    private fun openDevice(device: UsbDevice, conn: UsbDeviceConnection): UBCommand? {
        val serialPort = UsbSerialDevice.createUsbSerialDevice(device, conn)
        if (serialPort.open()) {
            val engine = UBCommandImpl(serialPort, connectTimeout.toLong())
            serialPort.setBaudRate(baudRate)
            serialPort.setDataBits(dataBits)
            serialPort.setStopBits(stopBits)
            serialPort.setParity(parity)
            serialPort.setFlowControl(flowControl)
            //上面是设置些常用参数
            val reader = readFactory.create(engine)
            serialPort.read(reader)
            serialPort.getCTS(reader)
            serialPort.getDSR(reader)
            return engine
        }
        Logger.e(TAG, "open err ************* ================= *************")
        return null
    }

    private inner class UBCommandImpl(serialPort: UsbSerialDevice, connectTimeout: Long) : UBCommand(serialPort, connectTimeout) {

        override fun onServiceStateChanged(status: Int, link: Int) {
            Logger.i(TAG, "onServiceStateChanged $status, $link")
            if (!this.handleServiceStateChanged(status, link)) {
                Logger.i(TAG, "un handle onServiceStateChanged $status, $link")
                //未处理到的连接状态
                val device = devices[link]
                if (device == null) {// 没有设备一般就是此台设备当从设备时的情况, 或者连接前还没有加入设备
                    Logger.e(TAG, "此台设备被别的连上了... ")
                } else { //主动连,设备关了取消了再连上了, 或者又关上了
                    if (status == SUCCESS) {
                        device.status = STATUS_CONNECTED
                        // 是否需要再重新连
                    } else {
                        device.status = STATUS_INIT
                    }
                }
            }
            readCallbacks.forEach { it.onConnectStateChanged(link, status) }
        }

        override fun onRead(link: Int, bytes: ByteArray, start: Int, size: Int) {
            readCallbacks.forEach { it.onRead(link, bytes, start, size) }
        }

        override fun onScanResult(result: List<UBScanResult>) {
            scanCallbacks.forEach { it.onScanResult(result) }
        }
    }
}